package com.berchen;

import org.apache.log4j.Logger;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ConfigurableApplicationContext;
import java.lang.management.ManagementFactory;

/**
 * @Transactional
 * 事务注解默认发生RuntimeException异常会回滚事务，其他异常不会回滚事务。
 * 如果要指定异常回滚事务需要通过rollbackFor=IOException.class指定异常类型。
 *
 * @Transactional
 * public int update(Integer id ){
 *     // 插入数据AA
 *     new Thread(()->{
 *         // 插入数据BB
 *     }).start();
 * }
 * 上面这种情况，只有update方法是有事务管理的，新启的一个线程是没有事务管理的（新的一个连接）。
 * 当在方法内线程外抛出异常，方法外面的AA插入会回滚。但是BB会插入进去。
 * 当在线程内部抛出异常，两条插入 都会成功，因为线程的连接没有事务管理，抛出异常不会回滚，同时线程的异常不会影响到方法的事务。
 *
 * @Service
 * public class MyService{
 *     @Transactional       当加了这个注解之后，通过对象调用该方法的时候是带来对象调用的（排除掉非代理对象调用）。
 *     public void a(){}
 *
 *     public void b(){
 *         a();
 *     }
 * }
 * 当我们controller调用b方法的时候，这时候 a b 方法都是没有事务的，因为此时调用b方法的是一个普通对象，不是一个代理对象，同时b方法中调用a方法也是一个普通对象。
 * 当controller直接调用a方法的时候，是有事务的，此时是代理对象调用。
 *
 * @Service
 * public class MyService{
 *
 *     @Autowrie
 *     private HeService heService;
 *     public void a(){
 *         heService.b();
 *     }
 * }
 * @Service
 * public class HeService{
 *     @Transactional
 *     public void b(){}
 * }
 * 当我们controller调用a方法的时候，a方法是没有事务的（没有@Transactional注解，就不是代理对象调用）。但是b方法是有事务的，因为b方法是代理对象调用的。
 *
 * private 、final、static 方法不能加@Transactional注解。
 * @Transactional注解只有在public方法上才生效。动态代理拦截不到protected和default修饰的方法。
 *
 * spring事务管理失效的情况？
 * 1、@Transactional注解的方法不是public。
 *      为什么？因为Spring事务在调用动态代理之前就会非public方法进行了过滤，非public方法就不生效。
 * 2、方法抛出的异常不是RuntimeException
 * 3、方法抛出异常时，使用try....catch进行了捕获。而且catch语句块没有throw new RuntimeException异常。
 * 4、@Transactional注解所在类没有纳入spring容器管理
 * 5、MYSQL存储引擎采用的是MYISAM(不支持事务)而不是InnoDB.
 * 6、@Transactional注解修饰的方法被该类内部的方法直接调用（这时候不是代理对象调用）。
 * 7、@Transactional的Propagation属性设置为NOT_SUPPORTED传播机制。
 *
 * spring的事务传播行为是两个有事务的方法进行相互调用的时候的事务如何进行传播？
 * spring的事务传播主要是看被调用的方法设置的一个事务传播行为是什么？
 * ServiceA的methodA() ServiceB的methodB()
 * 当methodA调用methodB的时候：
 * 设置methodB方法事务传播行为：REQUIRED（有事务就以当前事务执行，没事务就创建事务执行）
 * 1、当methodA有事务的时候，调用B的时候，这时候B直接加入到A事务中，只有A\B任意一个方法出现异常（RuntimeException或自定义设置了的）都会回滚。
 * 2、当methodA没有事务的时候，调用B的时候，这时候B会新创建一个事务，当B出现异常的时候会导致B回滚，不会导致A回滚，同时A出现异常对A\B方法没有影响不会回滚。
 * 设置methodB方法事务传播行为：REQUIRED_NEW（如果A有事务，就讲A事务挂起，新建事务运行。）、NESTED（如果A有事务，就将A挂起，同时新建事务设置savepoint，等B执行完成再继续执行A）。
 * 1、如果B已经成功提交事务，A出现异常，只会回滚A。
 * 2、如果B发生异常并且回滚，A对B的异常进行try...catch处理，那么A不会回滚，A不处理B的异常时，那么A也会回滚。
 * 3、REQUIRED_NEW（完全一个新事务，与外部事务相互独立），NESTED（外部事务子事务，外部事务commit，嵌套事务也会被commit，外部事务rollback，嵌套事务也会被rollback）。
 *
 * 为什么CGLIB动态代理的类不能是最终类？
 * CGLib采用了非常底层的字节码技术，其原理是通过字节码技术为一个类创建子类，并在子类中采用方法拦截的技术拦截所有父类方法的调用，顺势织入横切逻辑。
 * （利用ASM开源包，对代理对象类的class文件加载进来，通过修改其字节码生成子类来处理）
 *
 *
 * 在同一个事务方法中，可以查询到前面update的数据吗？
 * 1、可以的。当前面update之后数据库还没有更新，但是后面的查询时能拿到新的数据的。
 *
 * @Async标记的方法在被调用的时候会异步执行（新开一个线程执行）。
 * 当@Async标记的方法被@Async标记的方法调用的时候会失效。
 *
 * @Conditional注解什么作用：用来当作条件判断.
 * 自定义类实现Condition，该接口的方法返回值为true就条件判断为true。就加载配置类。否则就不加载配置类。
 *
 * 强一致性：写入什么就读出什么，用户体验好，但是对系统性能影响大。
 * 弱一致性：系统数据不实时一致，写入成功后，不保证立即可以读出写入的数据，（也不保证多久能读取到），会尽可能保证在某个时间级别（一般秒级）后，数据能够达到一致。
 * 最终一致性：最终一致性是弱一致性的一种，系统会保证在一定时间内，能够达到一致。也是弱一致性中比较推崇的一种数据一致性方式。
 * ACID：数据库的强一致性理论。
 * CAP：一致性、可用性、分区容错性。
 * BASE：
 * 1、基本可用（指分布式系统在出现不可预知故障的时候，允许损失部分可用性）。
 * 2、软状态（指允许系统中的数据存在中间状态，允许系统在不同节点的数据副本之间进行数据同步的过程存在延时）
 * 3、最终一致性（指的是所有数据副本，在经过一段时间的同步之后，最终都能够达到一个一致的状态，而不需要实时保证系统数据的  强一致性）。
 *
 * ClassNotFoundException：是一个异常，当动态加载Class的时候找不到类会抛出该异常。一般在Class.forName()、ClassLoader.loadClass()、ClassLoader.findSystemClass()的时候抛出。
 * NoClassDefFoundError：是一个错误，当编译成功以后执行过程中Class找不到会抛出该错误。由JVM抛出。
 *
 * springboot2.6之前默认支持循环依赖，之后需要spring.main.allow-circular-references=true配置来开启循环依赖。
 *
 * mybatis的SQL语句调用java代码：
 * insert into t_goods(id,name,name_pinyin) values(#{id},#{name},"${@com.berchen.utils.PinyinUtils@toPinYin(name)}")
 *
 * StringBuffer 默认长度16（扩容规则为2*16+2，依次类推） 如果指定长度（X）
 * 添加元素(str)超过长度X 并且if(str.length>(2*x+2)) sb的容量=str.length,否则sb的容量为2*x+2
 *
 * jdk7和jdk8的HashMap的变化（底层）：
 * 1、7中底层使用的是数组+链表。8中使用的是数组+链表+红黑树。加入红黑树的目的是提高HashMap插入和查询整体效率。
 * 2、7中链表插入元素使用的是头插法。8中使用的是尾插法，因为8中插入元素需要判断链表的元素个数（就需要遍历链表），所以就遍历到链表尾部，就直接在尾部插入。
 * 3、7中哈希算法比较复杂，存在各种右移和异或运算（这样得到的哈希值的散列性更好）。8中进行了简化，因为负载的哈希算法的目的就是提高散列性，来提供HashMap的整体效率，而8中新增了红黑树，所以可以适当的简化哈希算法节省CPU资源。
 *
 * jdk7和jdk8的JVM的变化：
 * 1、7中存在永久代。8中没有永久代，替换它的是元空间，元空间占的内存是本地内存空间，这么做的原因是不管永久代还是元空间，他们都是方法区的具体实现，之所以元空间占的内存是内地内存，官方的说法是为了和JRockit统一，
 *  不过额外还有一些原因，比如方法区存储的类信息通常比较难确定的，所以对于方法区的大小是比较难指定的，太小了容易出现方法区溢出，太大了又会占用太多虚拟机的内存空间，而转移到本地内存后则不会影响虚拟机所占用的内存。
 *
 * JVM元空间的特点及作用？
 * 1、JDK8才叫元空间，之前叫方法区/永久代。
 * 2、线程共享和堆一样。
 * 3、存储类信息、常量、静态变量、常量池、即时编译后的代码等数据。
 * 4、元空间采用本地内存，本地内存有多少剩余空间，它就能扩展到多少，也可以设置元空间大小。
 * 5、元空间很少有GC垃圾收集，一般该区域回收条件苛刻，GC很少来回收。
 * 6、元空间内存不足时，将要抛出OutOfMemoryError。
 *
 * JVM栈的特点及作用？
 * 1、线程私有
 * 2、方法执行创建栈帧，存储局部变量等信息。
 * 3、方法执行入虚拟机栈，方法执行完成出虚拟机栈，（先进后出）。
 * 4、栈深度大于虚拟机所允许StackOverflowError
 * 5、栈需扩展而无法申请空间 OutOfMemoryError（比较少见）
 * 6、栈里面允许方法，存储方法的局部变量名，变量名所指向的值（常量值、对象指）都存放在堆
 * 7、栈一般不设置大小，栈所占空间其实很小，可以通过-Xss1M,设置，不设置默认1M
 * 8、随着线程而生，随线程而灭
 * 9、该区域不会有GC回收。
 *
 * JVM堆的特点及作用？
 * 1、线程共享
 * 2、虚拟机启动时创建
 * 3、虚拟机所管理的内存中最大的一块区域
 * 4、存放所有实例对象或数组
 * 5、GC垃圾收集器的主要管理区域
 * 6、可分为新生代、老年代
 * 7、新生代更细化为Eden、From Survivor、To Survivor
 * 8、可通过-Xmx、-Xms调节
 *
 * 如何手写一个热加载？
 * 1、写一个类加载器（继承ClassLoader），用于加载自己定的目录的java文件
 * 2、写一个观察狗（实现Runable），观察文件是否发生变化。
 * 3、启动项目，当文件发生变化的时候就会被检测变化，就会重新加载。
 *
 * JVM优化究竟优化什么？
 * 1、尽可能让对象都在新生代里分配和回收，避免对象频繁进入老年代导致老年代频繁垃圾回收。
 * 2、给系统足够的内存空间，避免新生代频繁的进行垃圾回收
 * 3、指定合适的垃圾收集器。
 *
 * 什么是接口幂等性：一个接口调用多次没有副作用（扣款接口，调用多次只扣款一次）。
 * 接口幂等性在什么场景会出现：用户重复操作，代码重试，消息重复消费，网络波动。
 * 解决幂等性：唯一索引去重、token+redis、状态机、乐观锁、分布式锁、全局唯一等。这些都如何实现？
 *
 * mysql索引：
 * int类型ID 条件 where id='1'、where id=1 都会使用到索引 '1'会隐式转成1.
 * varcahr类型ID 条件 where id='1' 使用到索引，where id=1 不会使用到索引。
 *
 * Spring加载Bean到容器：
 * 1、@Component
 * 2、@Bean
 * 3、@Import(User.class)
 * 4、xml配置
 *
 * 先更新数据库再更新缓存的问题？有可能会导致数据不一致问题。
 * A操作去读取数据先去redis（假设redis现在还没有数据）拿，但是redis没有数据，就去数据库中拿，拿到name=张三，这时候就需要将数据保存到redis。
 * 但是A还没有将name保存到redis，这时候B请求来更新数据将数据库的name update lisi ，然后去redis删除key。
 * 然后A将name保存到redis，这时候A请求的name=张三，这就导致了数据库和redis数据不一致问题。
 * 如何保证数据一致性呢？
 * 1、删除缓存
 * 2、更新数据库
 * 3、休眠一定时间（200毫秒）
 * 4、再一次删除缓存。
 *
 * 什么是redis大key？
 * 1、String类型的数据控制在10KB以内，之上的就是大KEY。
 * 2、hash,list,set,zset元素个数不要超过5000个，超过就是大key
 * 如何发现redis大key？
 * redis-cli -h 127.0.0.1 -p6379 -a "password" --binkeys
 * 如何删除redis大key？
 * 1、主动删除：unlink key
 * 2、惰性删除，conf中配置：
 * lazyfree-lazy-expire on # 过期惰性删除
 * lazyfree-lazy-eviction on    # 超过最大内存惰性删除
 * lazyfree-lazy-server-del on  # 服务端被动惰性删除
 *
 * 如何提升接口的响应速度？
 * 1、集群横向扩展，分摊每台服务器的请求量。
 * 2、减少接口中要处理的业务，非核心业务异步处理。
 * 3、接口中引入缓存，提升数据加载速度。
 * 4、采用池化技术（redis、mysql、对象）
 * 5、JVM、数据库、Linux、网络等优化。
 *
 * select、poll、epoll的区别？
 * 1、select模型，使用的是数组来存储socket连接文件描述符，容量固定，有最大连接数的限制，需要通过轮询来判断是否发生了IO事件。
 * 2、poll模型，使用的是链表来存储socket连接文件描述符，容量不固定。没有最大连接数的限制，需要通过轮询来判断是否发生了IO事件。
 * 3、epoll模型，采用事件通知模型，当发生了IO事件，应用程序才进行IO操作，不需要主动去轮询。
 *
 * select ... for update是锁表还是锁行？
 * 结论：当select的where中用到了索引，那么这就是锁行。当select的where中没有用到索引，那么这就是锁表。
 * select * from t_user where id=1 for update;  如果id是索引，那么只锁id=1的这一行数据，其他行数据还是可以操作。如果id不是索引，那么整张表都被锁住，整张表数据都不能操作。
 * for update是一个排他锁。
 *
 * 什么是索引下推？
 * 数据库从上到下有：连接层->server层->引擎层（InnoDB、MyISAM）->文件系统层。数据库的索引条件过滤在引擎层，非索引条件过滤在server层。
 * 有一个表有联合索引（name,age）
 * 当我们查询 select id,name,age from user where name like '张%' and age=18;
 * 当我们不使用索引下推时：在引擎层中根据name找到张开头的id值，然后将id值去聚簇索引中拿到数据返（这就是回表操作，有几个ID就会多少次表）回到server层，然后server将返回的数据根据age=18再次进行过滤。
 * 当我们使用索引下推时：在引擎层根据name和age的联合索引找到数据对应的ID，然后回表根据ID值查询数据返回给server层。
 * 索引下推的作用就是减少回表查询次数，提高查询效率。
 *
 * 守护线程会随着main线程的结束而结束。设置线程为守护线程：thread.setDaemon(true);
 * linux 最大创建线程数量 /proc/sys/kernel/threads-max文件中配置。
 *
 * JVM线程属于用户态还是内核态？内核态。
 *
 * JVM堆为什么要分为年轻代和老年代？
 * 垃圾回收的性能开销和内存空间利用率的考虑。
 * 年代划分就可以采用不同的垃圾回收算法进行垃圾回收。
 *
 * JVM堆的年轻代为什么要有两个Survivor区？
 * 没有survivor区的话JVM的性能会降低（频繁创建对象，进行minor gc ，将存活的对象移入老年代，老年代就会满，就会频繁进行full GC就会降低性能）。
 * 有一个survivor区的话性能下降。（存活的对象到survivor中内存是不连续的:如果整理就会损耗性能，不整理就有内存碎片）
 *
 * tomcat为什么要打破类加载的双亲委派模型？
 * 1、部署在同一个tomcat上的两个web应用所使用的java类库要相互隔离。（项目1 使用spring3.0,项目2 使用spring5.0，在各自的项目目录下）
 * 2、部署在同一个tomcat上的两个web应用所使用的java类库要相互共享。（项目1 项目2 使用同一个server-api 在tomcat的lib目录下）
 * 3、保证tomcat服务器自身的安全不受部署的web应用程序影响。
 * 4、需要支持JSP页面的热部署和热加载（自定义类加载器）。
 *
 * fail-safe和fail-fast机制分别有什么作用？是多线程并发操作集合时的一种失败处理机制。
 * 1、fail-safe（快速失败）：在集合遍历过程中一旦发现容器中的数据被修改过了就会立刻抛出一个异常（ConcurrentModificationException），从而导致整个遍历过程是失败的。
 * java.util包下的集合类都是属于快速失败机制的。常见的ArrayList和HashMap
 * Map<> map=new HashMap<>();
 * Iterator iterator=map.keySet().iterator();
 * while(iterator.hasNext()){
 *     map.put("key","value");  // 在快速失败模式下，这里会出现问题
 * }
 * 2、fail-fast（安全失败），出现集合元素的修改不会抛出ConcurrentModificationException异常。
 * 采用安全失败的集合容器在遍历的时候不是直接在集合内容上进行访问，而是先复制原有集合的内容，而在拷贝的集合上去进行遍历
 * 由于迭代器是对原始集合的拷贝去进行遍历的，所以在遍历过程中对原始集合的修改并不能被迭代器检测到，所以就不会出现异常。
 * java.util.concurrent包下的集合容器都是输入安全失败的。
 * CopyOnWriteArrayList<> list=new CopyOnWriteArrayList<> ();
 * Iterator iterator=list.iterator();
 * while(iterator.hasNext()){
 *     Integer i =iterator.next();
 *     if(i==7){
 *         list.add(15);// 在安全失败模式下，这里不会出现问题
 *     }
 * }
 *
 * mysql执行过程？
 * 客户端发送一条SQL到mysql，先去缓存中查询（缓存默认关闭，在配置文件中开启）。当缓存中没有，就去解析器解析SQL语句生成语法树，然后优化器就去优化SQL（找到最快的一条执行计划）。
 * 然后执行引擎去执行这个执行计划。执行的时候就会调用底层的存储引擎。然后去磁盘上拿到数据，数据返回放入缓存（缓存开启的情况）。
 *
 * mysql体系架构？
 * 连接池：验证连接用户名和密码是否正确。
 * SQL接口：接收客户端发送过来的SQL。
 * 解析器：将SQL解析为一个语法树。
 * 优化器：将SQL进行一个优化，找到最优的方式去查询数据。
 * 缓存：（默认关闭），相同的SQL会缓存在这个池中。
 * 存储引擎：通过存储引擎去执行我们最优的查询。可插拔式的存储引擎，MYSQL的一个特性。5.5及以后默认innodb。之前myisam。
 * 磁盘数据：通过存储引擎来管理的。
 *
 * InnoDB存储引擎运行原理？
 * 1、将需要更新的记录从磁盘文件加载到缓冲池（如果之前加载过就不用加载了）。 status=12
 * 2、缓冲池将旧值写入到undo.log日志文件中（该日志文件用来记录旧数据，便于回滚）。status=12
 * 3、执行器更新内存中的数据到缓冲池中。status=1
 * 4、执行器将新数据写入redo log buffer中。
 * 5、准备提交事务将redo log buffer中的日志写入到redo.log日志文件中。
 * 6、执行器准备提交事务将binlog日志写入磁盘。
 * 7、将binlog文件与位置和commit提交标记写入redo.log日志文件
 * 8、IO线程将缓冲池数据写入到磁盘文件。
 *
 * mysql数据库InnoDB死锁？
 * 事务1 给id=1的数据加独占锁 执行修改
 * 事务2 给id=2的数据加独占锁 执行修改
 * 事务1 接着去给id=2的数据加锁 加锁失败
 * 事务2 接着去给id=1的数据加锁 加锁失败
 * 在mysql中 begin 开启一个事务，执行SQL就会去加锁，当事务提交就会释放锁。
 *
 * mysql的三种日志？
 * 1、undo log:事务回滚日志。将旧数据写入到这个日志文件中，当要回滚的时候就将数据拿出去覆盖。
 * 2、redo log:事务前滚日志。将新数据写入这个日志文件中，如果mysql宕机，可以从redo日志中恢复之前的修改，不至于修改丢失。
 * 3、bin log:主从复制日志。有三种保存方式：1、记录SQL语句（可能会有数据不一致问题）。2、记录每一行的变更。3、前两种方案的折中选择。
 *
 * mysql数据库InnoDB的buffer pool结构？
 * 1、默认大小128M。可以通过配置修改。
 * 2、当我们要对数据进行删改的时候，会将数据加载到缓冲池中。mysql对磁盘的数据做了进一步的抽象，数据是以数据页的形式存在。每个数据页是16KB；
 * 3、数据页在缓冲池中对应叫缓存页，同时每个缓存页有一个元数据信息（该缓存页的描述信息，缓存页大小的5%）。
 * 4、缓冲池中还有一个free 链表，是一个双向链表，就是元数据链表（这里元数据链表对应的缓存页都是空的）。同时还有一个基础节点记录当前还有多少空闲的缓存页。
 *  当添加一条数据的时候就找到一个元数据节点，找到对应的缓存页将数据加载进去，同时将该元数据的前后指针去掉。同时基础节点数量-1.这样元数据链表就是一个空闲链表。
 * 5、缓存页用完了怎么办？最近最少使用（LRU）算法淘汰。
 *  我们每次加载一次数据到缓存页或读取一次缓存，都会将缓存页的元数据放到LRU链表的头部，尾部就是最近没有使用的，淘汰的就是尾部的元素对应的缓存页。
 * 6、flush链表的作用？当我们对缓存页的数据进行增删改的时候，这时候缓存页的数据就变成脏数据了，就将缓存页对应的元数据信息记录到flush链表中，这样我们将脏数据刷入磁盘（提交事务）的时候才知道刷入那些数据。
 *
 * mysql前缀索引？
 * 前缀索引也叫局部索引，也就是给某个字段的部分信息添加索引叫做前缀索引。
 * alert table 表名 add index (phone(7))
 * 1、前缀索引能减小索引文件大小，让每个索引页可以保存更多的索引值，从而提高了索引查询的速度。
 * 2、前缀索引不能用于order by、group by 也不能用于覆盖索引。
 * 3、如果前缀索引所选定的前几个字符重复概率较低，则前缀索引效率高。
 *
 * docker与虚拟机有什么区别？
 * 1、虚拟机是在硬件的基础上实现虚拟化，在一台电脑的硬件资源基础上安装多个虚拟机，每个虚拟机里面单独安装一个操作系统。
 * 2、docker容器是操作系统层面的虚拟化，直接复用本地主机的操作系统，不需要单独安装操作系统，一个操作系统上可以运行成百上千个容器。
 *
 * 主流注册中心对比：
 * Eureka ：AP 保证可用性 对外暴露接口http   当eureka集群中有一台机子挂掉，还是能往集群中注册服务
 * zookeeper：CP 保证一致性 对外暴露客户端   当ZK集群中有一台机子挂掉，不能往集群中注册服务。
 * nacos：AP/CP切换，可以在可用性和一致性两者进行切换。
 *
 * Dubbo容错策略：
 * 1、失败重试调用其他服务。
 * 2、快速失败，只发起一次调用，失败了就失败了。
 * 3、安全失败，失败了就忽略异常。
 * 4、自动恢复，记录失败请求，定时重发。
 * 5、并行调用所有服务器，只要一个成功就成功，
 * 6、广播调用所有服务器，只要一个失败就失败。
 *
 * Dubbo协议：
 * 1、dubbo:// 最常用的，TCP长连接
 * 2、rmi://     TCP短连接
 * 3、hession:// HTTP短连接
 * 4、http://    HTTP短连接
 * 5、webservice://  HTTP短连接
 * 6、thrift://      RPC框架
 * 7、grpc://        GRPC
 * 8、redis://
 * 9、memcached://
 * 10、rest://
 *
 * HTTP和websocket的区别？
 * 1、HTTP：客户端给服务端发送一个请求，服务端给客户端一个响应，此时短连接就关闭了。
 * 2、websocket：客户端给服务器一个请求，服务器给客户端一个响应，此时就建立了一个长连接，然后客户端和服务端就可以进行多次的双向通信。
 *
 * websocket使用场景？
 * 1、websocket聊天室。
 * 2、股票实时价格显示等应用。
 * 3、即时通讯、游戏，可视化大屏展示等领域。
 * 4、企业内部通讯 等功能，主要通讯协议是websocket。
 * 5、web网页聊天，客服系统实现。
 * 6、系统提醒、用户上下线提醒、实时数据更新、用户在线状态、消息通知、扫描二维码登录/二维码支付、弹幕、在线选座等。
 *
 * Maven依赖原则：
 * 1、最短路径优先原则。A依赖项目B和C， B依赖项目D， C、D都依赖spring.jar 这时候A依赖的是C的spring.jar
 * 2、pom文件中声明顺序优先。
 * 3、覆写优先原则。
 *
 * redis中失效KEY是如何删除的？
 * 1、消极策略：当访问KEY的时候发现失效，就删除。
 * 2、积极策略：
 *  redis周期性的检测（一秒10次）。
 *  具体操作：
 *      2.1 随机测试20个带过期信息的KEY（样本）。
 *      2.2 删除其中已经过期的KEY。
 *      2.3 如果超过25%的KEY删除，就立刻重复执行步骤2.1；
 *
 * 什么是垃圾回收担保机制？
 * 在发生minor GC（新生代GC）之前，虚拟机会检测老年代最大可用空间 > 新生代所有对象总大小？
 * 1、满足，那么直接进行minor GC。
 * 2、不满足，那么虚拟机会判断 老年代最大可用空间 > 历代晋升到老年代对象的平均大小？
 * 2.1 满足 尝试进行一次minor GC。
 * 2.2 不满足，直接进行一次full GC 然后进行 minor GC。
 *
 * Class.forName("com.berchen.Test")和ClassLoader的区别？
 * 字节码 -> 类加载 -> 链接（验证 --> 准备 --> 解析） -> 初始化 -> 使用 -> 卸载
 * 1、Class.forName加载类时，类会被初始化。
 * 2、ClassLoader加载类时，类不会被初始化。ClassLoader.getSystemClassLoader().loadClass("com.berchen.Test");
 *
 * TCP协议可能发生粘包和拆包：
 * 粘包：客户端发出两个消息，但是服务端只接收到一个消息。发出：123 456 接收：123456
 * 拆包：客户端发出一个消息，但是服务端收到两个消息，发出：123456 接收：123 456
 * 如何解决粘包：使用Netty的编码解码器可以解决。
 *
 * UDP是否会发生粘包拆包？
 * 1、UDP不会发生粘包拆包。
 * 2、UDP协议的数据包之间是没有联系、有明确边界的，一个数据包就是一条消息。
 * 3、如果一个数据包过大，超过路由器的封包大小，发现不能进行完整的传输，那么这个数据包就会丢失。
 * 4、UDP可能会发生丢包和数据包顺序错乱。如果数据包不完整在传输过程中就会丢弃，能收到的都是完整正确的包，另外收到的数据包顺序和发送的顺序可能不一样。
 *
 * redis如何根据KEY在集群中查找value？
 * 1、使用CRC16算法对KEY进行hash运算得到一个hash值。
 * 2、使用KEY的hash值对16384取余，得到具体的一个槽位。
 * 3、然后根据槽位和集群节点的映射信息，找到具体的节点。
 * 4、然后在节点上根据KEY读取到具体的value。
 *
 * Myisam 和 InnoDB的区别：
 * 1、myisam不支持行锁，InnoDB支持行锁。
 * 2、myisam不支持外键，InnoDB支持外键。
 * 3、myisam不支持事务，InnoDB支持事务。事务的好处就是并发环境下处理的效率和安全性。
 * 4、myisam的读效率比InnoDB高。
 * 5、myisam的写效率比InnoDB低。
 *
 * ArrayList 默认长度 10 扩容：10+10/2；
 * HashTable 默认长度 11 扩容：11*2+1
 * HashMap   默认长度 16 扩容：2的n次方
 *
 * Redis的NIO多路复用是什么？（IO多路复用模型）
 *  此模型用到select和poll函数，这两个函数也会使进程阻塞，select先阻塞，有活动套接字才返回，但是和阻塞I/O不同的是，这两个函数可以同时阻塞多个I/O操作，
 *  而且可以同时对多个读操作，多个写操作的I/O函数进行检测，直到有数据可读或可写（就是监听多个socket）。select被调用后，进程会被阻塞，内核监视所有select负责的socket，
 *  当有任何一个socket的数据准备好了，select就会返回套接字可读，我们就可以调用recvfrom处理数据。
 *  正因为阻塞I/O只能阻塞一个I/O操作，而I/O复用模型能够阻塞多个I/O操作，所以才叫做多路复用。
 *
 * 年轻代对象年龄到达15就移动到老年代中？为什么设置为15，还能不能设置更大？
 * 因为 对象头中 采用4个bit位来保存年龄，4个bit位能表示的最大数就是15！
 *
 * Redis的zset的底层数据结构是什么？（两种实现）
 * 1、ziplist：压缩双向链表。
 * 2、skiplist：跳表。
 *
 * ConcurrentHashMap如何保证线程安全？
 * 1。7：由多个segment组成，对每一个segment进行加锁，锁分离，不再整体加锁，使锁的粒度变小。
 *  多个Segment之间可以并发操作，一个Segment（类似HashTable）里面通过分段锁实现线程安全。
 *
 * i++ 为什么不是原子的？
 *  因为i++操作经过反汇编之后对应多条命令（取值，加1，赋值给变量），线程在执行每一条指令前都可能失去CPU的执行权。
 *
 * 要求多个线程执行完毕之后才执行后面的流程？
 * 1、通过CountDownLatch来实现。
 * 2、通过CompletableFuture来实现。
 * 3、通过join来实现。
 *
 * distinct和group by的区别？
 * distinct 主要用来做去重的。而group by的设计目的则是用来聚合统计的（也可以实现去重，但是效率没有distinct高）。
 *
 * 对象存储OSS为什么比磁盘存储快？
 * 卡夫卡的稀疏索引是什么？
 *  运行时异常和检查异常？
 */
@EnableCaching
@MapperScan({"com.berchen.busidb.dao","com.berchen.passdb.dao"})
@SpringBootApplication
public class ChenSystemApplication implements CommandLineRunner , ApplicationRunner {

    private static Logger logger=Logger.getLogger(ChenSystemApplication.class);
    public static void main(String[] args) {

        // 这里是启动，返回值就是IOC容器
        ConfigurableApplicationContext ioc = SpringApplication.run(ChenSystemApplication.class, args);
        logger.info("chen-system is started");

        System.out.println("当前线程个数：" + ManagementFactory.getThreadMXBean().getThreadCount());
        /*while (true) {
            System.out.println("当前线程个数：" + ManagementFactory.getThreadMXBean().getThreadCount());
            System.out.println("当前线程组个数：" + Thread.activeCount());   //
            try {
                Thread.sleep(6000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }*/
    }


    @Override
    public void run(String... args) throws Exception {

        logger.info("CommandLineRunner 的run 方法，该方法会执行一次 在程序启动完成之后");
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        logger.info("ApplicationRunner 的run 方法，该方法会执行一次 在程序启动完成之后");
    }
}
